<template>
    <div class="observe-container">
        <h1>DOM 变化监听演示</h1>
        <div class="info-panel">
            <p>编辑下方内容，观察 DOM 变化记录</p>
            <div class="stats">
                <span>总变化数: {{ changeCount }}</span>
                <button @click="clearChanges">清空记录</button>
            </div>
        </div>

        <!-- 可编辑区域 -->
        <div class="edit-area" ref="observeDOM" contenteditable="true" @input="handleInput">
            <p>在这里编辑内容，我会监听所有变化...</p>
        </div>

        <!-- 变化记录面板 -->
        <div class="changes-panel">
            <h3>DOM 变化记录</h3>
            <div class="change-item" v-for="(change, index) in changes" :key="index" :class="`change-${change.type}`">
                <div class="change-header">
                    <span class="change-type">{{ change.type }}</span>
                    <span class="change-time">{{ change.timestamp }}</span>
                </div>
                <div class="change-content">
                    <div v-if="change.oldValue" class="old-value">
                        <strong>旧值:</strong> <code>{{ change.oldValue }}</code>
                    </div>
                    <div v-if="change.newValue" class="new-value">
                        <strong>新值:</strong> <code>{{ change.newValue }}</code>
                    </div>
                    <div v-if="change.target" class="target-info">
                        <strong>目标:</strong> {{ change.target }}
                    </div>
                    <div v-if="change.attribute" class="attribute-info">
                        <strong>属性:</strong> {{ change.attribute }}
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from "vue";

// DOM 引用
const observeDOM = ref(null);
// 变化记录
const changes = ref([]);
// 变化计数
const changeCount = ref(0);
// MutationObserver 实例
let observer = null;

/**
 * 记录 DOM 变化
 * @param {string} type - 变化类型
 * @param {Object} detail - 变化详情
 */
const recordChange = (type, detail = {}) => {
    const change = {
        type,
        timestamp: new Date().toLocaleTimeString(),
        ...detail,
    };

    changes.value.unshift(change); // 新记录在前
    changeCount.value++;

    // 限制记录数量
    if (changes.value.length > 50) {
        changes.value = changes.value.slice(0, 50);
    }
};

/**
 * 处理输入事件
 */
const handleInput = (event) => {
    recordChange("input", {
        target: "textContent",
        newValue: event.target.textContent,
    });
};

/**
 * 初始化 MutationObserver
 */
const initObserver = () => {
    if (!observeDOM.value) return;

    // 配置观察选项
    const config = {
        attributes: true, // 观察属性变化
        childList: true, // 观察子节点变化
        characterData: true, // 观察文本内容变化
        subtree: true, // 观察所有后代节点
        attributeOldValue: true, // 记录属性旧值
        characterDataOldValue: true, // 记录文本旧值
    };

    // 创建观察者实例
    observer = new MutationObserver((mutationsList) => {
        mutationsList.forEach((mutation) => {
            console.log("mutation");
            console.log(mutation);
            switch (mutation.type) {
                case "childList":
                    mutation.addedNodes.forEach((node) => {
                        recordChange("childAdded", {
                            target: node.nodeName,
                            newValue: node.textContent || node.nodeName,
                        });
                    });
                    mutation.removedNodes.forEach((node) => {
                        recordChange("childRemoved", {
                            target: node.nodeName,
                            oldValue: node.textContent || node.nodeName,
                        });
                    });
                    break;

                case "attributes":
                    recordChange("attribute", {
                        target: mutation.target.nodeName,
                        attribute: mutation.attributeName,
                        oldValue: mutation.oldValue,
                        newValue: mutation.target.getAttribute(mutation.attributeName),
                    });
                    break;

                case "characterData":
                    recordChange("characterData", {
                        target: mutation.target.nodeName || "text",
                        oldValue: mutation.oldValue,
                        newValue: mutation.target.textContent || mutation.target.data,
                    });
                    break;
            }
        });
    });

    // 开始观察
    observer.observe(observeDOM.value, config);

    recordChange("observerStarted", {
        message: "开始监听 DOM 变化",
    });
};

/**
 * 清空变化记录
 */
const clearChanges = () => {
    changes.value = [];
    changeCount.value = 0;
    recordChange("cleared", {
        message: "清空了变化记录",
    });
};

/**
 * 停止观察
 */
const stopObserver = () => {
    if (observer) {
        observer.disconnect();
        observer = null;
        recordChange("observerStopped", {
            message: "停止监听 DOM 变化",
        });
    }
};

// 生命周期钩子
onMounted(() => {
    console.log("Observe component mounted");
    initObserver();
});

onUnmounted(() => {
    stopObserver();
});
</script>

<style scoped lang="scss">
.observe-container {
    padding: 20px;
    max-width: 1200px;
    margin: 0 auto;
}

.info-panel {
    background: #f5f5f5;
    padding: 15px;
    border-radius: 8px;
    margin-bottom: 20px;

    .stats {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-top: 10px;

        button {
            padding: 8px 16px;
            background: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;

            &:hover {
                background: #0056b3;
            }
        }
    }
}

.edit-area {
    border: 2px solid #ddd;
    border-radius: 8px;
    padding: 20px;
    min-height: 150px;
    margin-bottom: 30px;
    background: white;
    transition: border-color 0.3s;

    &:focus {
        outline: none;
        border-color: #007bff;
    }

    p {
        margin: 0 0 10px 0;
    }
}

.changes-panel {
    background: #f9f9f9;
    border-radius: 8px;
    padding: 20px;
    max-height: 400px;
    overflow-y: auto;

    h3 {
        margin-top: 0;
        color: #333;
    }
}

.change-item {
    background: white;
    border-radius: 6px;
    padding: 12px;
    margin-bottom: 10px;
    border-left: 4px solid #ddd;

    &.change-input {
        border-left-color: #28a745;
    }

    &.change-attribute {
        border-left-color: #007bff;
    }

    &.change-childAdded {
        border-left-color: #ffc107;
    }

    &.change-childRemoved {
        border-left-color: #dc3545;
    }

    &.change-characterData {
        border-left-color: #6f42c1;
    }
}

.change-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 8px;

    .change-type {
        font-weight: bold;
        color: #495057;
    }

    .change-time {
        color: #6c757d;
        font-size: 12px;
    }
}

.change-content {
    font-size: 14px;

    .old-value,
    .new-value,
    .target-info,
    .attribute-info {
        margin: 4px 0;

        strong {
            color: #495057;
            margin-right: 8px;
        }

        code {
            background: #f1f3f4;
            padding: 2px 6px;
            border-radius: 3px;
            font-family: "Courier New", monospace;
        }
    }
}

/* 滚动条美化 */
::-webkit-scrollbar {
    width: 8px;
}

::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 4px;
}

::-webkit-scrollbar-thumb {
    background: #c1c1c1;
    border-radius: 4px;

    &:hover {
        background: #a8a8a8;
    }
}
</style>
